#include <animation/AnimationGroup.h>
#include <animation/AnimationFileFormat.h>
#include <system/Log.h>

namespace ne
{
    AnimationGroup::AnimationGroup(ResourceManager *pManager,
                                   const uint64_t id,
                                   const std::string &name,
                                   const std::string &fileName,
                                   const bool isManual,
                                   ManualResourceLoader *pLoader)
        : Resource(pManager, id, name, fileName, isManual, pLoader)
    {
        mResourceType = "Animation";
    }

    ResourcePtr AnimationGroup::prepare(const std::string &name,
                                        const std::string &fileName,
                                        const bool isManual,
                                        ManualResourceLoader *pLoader,
                                        const bool backgroundThread)
    {
        return ResourceManager::prepare(name, fileName, isManual, pLoader, backgroundThread);
    }

    ResourcePtr AnimationGroup::load(const std::string &name,
                                     const std::string &fileName,
                                     const bool isManual,
                                     ManualResourceLoader *pLoader,
                                     const bool backgroundThread)
    {
        return ResourceManager::load(name, fileName, isManual, pLoader, backgroundThread);
    }

    bool AnimationGroup::prepare(const bool backgroundThread)
    {
        return Resource::prepare(backgroundThread);
    }
            
    bool AnimationGroup::load(const bool backgroundThread)
    {
        return Resource::load(backgroundThread);
    }

    void AnimationGroup::optimise(const bool discardIdentityNodeTracks)
    {
        for (ResourceMapIterator it=mResourceMap.begin(); it!=mResourceMap.end(); ++it)
        {
            AnimationPtr pAnimation = it->second;
            pAnimation->optimise(discardIdentityNodeTracks);
        }
    }

    AnimationGroup::~AnimationGroup()
    {
    }

    bool AnimationGroup::loadImplement()
    {
        FILE *file = openFile("rb");
        if (!file)
        {
            Log::Out(LL_WARNING,
                "Open AnimationGroup (%s) file (%s) failed", getName().c_str(), getFileName().c_str());
            return false;
        }

        AnimationGroupHeader animationGroupHeader;
        if (!fread(&animationGroupHeader, sizeof(AnimationGroupHeader), 1, file))
        {
            closeFile(file);
            Log::Out(LL_WARNING,
                "Load AnimationGroup (%s) from file (%s) failed", getName().c_str(), getFileName().c_str());
            return false;
        }

        if (animationGroupHeader.mMagicNum != ANIMATION_FILE_MAGIC_NUMBER ||
            animationGroupHeader.mVersion != ANIMATION_FILE_VERSION)
        {
            closeFile(file);
            Log::Out(LL_WARNING,
                "Load AnimationGroup (%s) from file (%s) failed with wrong Magic or Version", getName().c_str(), getFileName().c_str());
            return false;
        }

        // Seek to AnimationFileNames
        fseek(file, animationGroupHeader.mAnimationFileNameOffset, SEEK_SET);

        char animationFileNames[animationGroupHeader.mNumAnimations * MAX_STRING_LENGTH_IN_ANIMATION_FILE];
        if (!fread(&animationFileNames, sizeof(char)*MAX_STRING_LENGTH_IN_ANIMATION_FILE, animationGroupHeader.mNumAnimations, file))
        {
            closeFile(file);
            Log::Out(LL_WARNING,
                "Create Animation for AnimationGroup (%s) from file (%s) failed", getName().c_str(), getFileName().c_str());
            return false;
        }
        closeFile(file);

        for (uint32_t i=0; i<animationGroupHeader.mNumAnimations; ++i)
        {
            AnimationPtr pAnimation = create(&animationFileNames[i * MAX_STRING_LENGTH_IN_ANIMATION_FILE], &animationFileNames[i * MAX_STRING_LENGTH_IN_ANIMATION_FILE]);
            pAnimation->load();
        }

        return true;
    }

    bool AnimationGroup::unloadImplement()
    {
        unloadAll(false);
        return true;
    }

    bool AnimationGroup::downloadImplement()
    {
        FILE *file = openFile("wb");
        if (!file)
        {
            Log::Out(LL_WARNING,
                "Create AnimationGroup (%s) file (%s) failed", getName().c_str(), getFileName().c_str());
            return false;
        }

        AnimationGroupHeader animationGroupHeader;
        memset(&animationGroupHeader, 0, sizeof(AnimationGroupHeader));

        animationGroupHeader.mMagicNum = ANIMATION_FILE_MAGIC_NUMBER;
        animationGroupHeader.mVersion = ANIMATION_FILE_VERSION;
        strncpy(animationGroupHeader.mName, mName.c_str(),
                MAX_STRING_LENGTH_IN_ANIMATION_FILE);
        animationGroupHeader.mNumAnimations = getResourceCount();
        fwrite(&animationGroupHeader, sizeof(AnimationGroupHeader), 1, file);
        animationGroupHeader.mAnimationFileNameOffset = ftell(file);
        fseek(file, 0, SEEK_SET);
        fwrite(&animationGroupHeader, sizeof(AnimationGroupHeader), 1, file);
        char animationFileNames[animationGroupHeader.mNumAnimations * MAX_STRING_LENGTH_IN_ANIMATION_FILE];
        ResourceMapConstIterator it = mResourceMap.begin();
        for (uint32_t i=0; i<animationGroupHeader.mNumAnimations; ++i)
        {
            AnimationPtr pAnimation = it->second;
            strncpy(&animationFileNames[i * MAX_STRING_LENGTH_IN_ANIMATION_FILE], pAnimation->getFileName().c_str(),
                    MAX_STRING_LENGTH_IN_ANIMATION_FILE);
        }

        fseek(file, animationGroupHeader.mAnimationFileNameOffset, SEEK_SET);
        fwrite(animationFileNames, sizeof(char)*MAX_STRING_LENGTH_IN_ANIMATION_FILE, animationGroupHeader.mNumAnimations, file);
        closeFile(file);

        return true;
    }

    size_t AnimationGroup::_calculateSize() const
    {
        size_t size = 0;
        for (ResourceMapConstIterator it=mResourceMap.begin(); it!=mResourceMap.end(); ++it)
        {
            size += it->second->getSize();
        }
        return size;
    }

    Resource* AnimationGroup::createImplement(const uint64_t id,
                                              const std::string &name,
                                              const std::string &fileName,
                                              const bool isManual,
                                              ManualResourceLoader *pLoader)
    {
        return new Animation(this, id, name, fileName, isManual, pLoader);
    }
}
